home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d26 / asmtut4.arc / CHAP25.DOC < prev    next >
Text File  |  1990-08-10  |  24KB  |  487 lines

  1.  
  2.  
  3.  
  4.                                                                            268
  5.  
  6.                             CHAPTER 25 - WHAT DOES IT ALL MEAN?
  7.  
  8.  
  9.              What does it all mean? Not a whole lot, actually. We can now save
  10.              memory space and we can save a lot of time. But with the
  11.              incessant march of technology these things mean less and less. A
  12.              few years ago, when 64k or 128k was a lot of memory and memory
  13.              was expensive, having a 20k program instead of a 40k program was
  14.              a significant advantage. Now it means almost nothing unless it is
  15.              a memory resident program. What about disk space? Just a while
  16.              back we were operating with two 360k floppy disks and a hard disk
  17.              was too expensive. Nowdays everyone has a 20meg hard disk.{1} And
  18.              speed? Those programs that were slow on an 8088 now seem o.k. on
  19.              an 80386. Those programs that were unbearably slow on the 8088
  20.              died a quick death and are no longer around.
  21.  
  22.              Compilers are better and they have more subroutines available.
  23.              They are also easier to program than going to the assembler
  24.              level. What this chapter is about is when NOT to use the standard
  25.              compiler functions and subroutines.
  26.  
  27.              First, you should understand that all compiler subroutines are
  28.              general purpose subroutines. They need to be all things to all
  29.              people. Imagine what a vehicle would be like if we gave the
  30.              designer the following specification:
  31.  
  32.                  We want to be able to drive to the store for groceries. It
  33.                  should be fuel efficient. In case we want to go into the
  34.                  mountains it should be an all terrain vehicle. We also want
  35.                  to be able to haul a roomful of furniture from coast to
  36.                  coast. Oh yes, and we want to be able to race it at Le Mans.
  37.  
  38.              Being universal requires a lot of code and it slows things down.
  39.              Whether this extra code and time is too much is a question you
  40.              need to decide for yourself. First, here are some examples of
  41.              size. This is a C program that does almost nothing:
  42.  
  43.                  #include <stdio.h> 
  44.                  main() 
  45.                  { 
  46.                          int     x ; 
  47.                          x = 27 ;                        /* line 1 */ 
  48.                          scanf  ( "%d", &x ) ;           /* line 2 */ 
  49.                          printf ( "%d\n", x ) ;          /* line 3 */ 
  50.                  } 
  51.              ____________________
  52.  
  53.                 1. Which has led to one of my pet peeves. All installation
  54.              programs for compilers and word processors dump EVERYTHING on the
  55.              hard disk. This gives us subdirectories that have 50 files in
  56.              them, and we don't have the foggiest notion of what any of the
  57.              files are for. If these installation programs would only prompt
  58.              us by type of file to find out what we want to install and want
  59.              to leave off the hard disk, we would all be better off.
  60.  
  61.              ______________________
  62.  
  63.              The PC Assembler Tutor - Copyright (C) 1990 Chuck Nelson
  64.  
  65.  
  66.  
  67.  
  68.              Chapter 25 - What Does It All Mean?                           269
  69.              ___________________________________
  70.  
  71.  
  72.              I have made 3 programs from this. LINE1.C has line1 only. LINE2.C
  73.              has lines 1 and 2. LINE3.C has lines 1, 2 and 3. For those non C
  74.              people, scanf is an input function, printf is an output function.
  75.              Guess how big each program is. Here's the directory listing.
  76.  
  77.                  LINE1    EXE     3176   6-22-90   8:48a 
  78.                  LINE2    EXE     7170   6-22-90   8:49a 
  79.                  LINE3    EXE     9134   6-22-90   8:49a 
  80.  
  81.              It takes 3000 bytes to start a C program (this is the startup
  82.              module) 4000 bytes more to enter something and 2000 bytes extra
  83.              to print something. That first 3000 bytes is unavoidable if you
  84.              are writing in a high level language. If you are doing a lot of
  85.              general purpose i/o, these extra amounts aren't too bad. There
  86.              are two cases where you might want to use your own i/o routines.
  87.              First, if you have something simple or secondly, if you have
  88.              something special, you want to do your own i/o.
  89.  
  90.              If you don't need all that flexibility, you are better off doing
  91.              your own i/o. Here are two files that write a text screen to a
  92.              disk file.
  93.  
  94.                  COPYSCRN EXE    10454   6-10-90   9:30a 
  95.                  INTSCRN  COM      445   6-12-90   7:40p 
  96.  
  97.              They both do the same thing except that the .COM file is a little
  98.              more sophisticated. Notice the difference in size. Speed really
  99.              doesn't play a part here because what they do is so simple that
  100.              it takes just a second in any case. The program was so simple
  101.              that it only took an hour or two to write, so I didn't lose any
  102.              time by writing it in assembler.
  103.  
  104.              The other case is when you have a specific idea of what the
  105.              screen should look like. You want control of the whole screen all
  106.              the time. This includes all word processors, databases,
  107.              programming environments, etc. They all take charge of the screen
  108.              because some DOS functions are too slow. If you remember from the
  109.              ZOOM chapter, there is a radical difference between what you can
  110.              do and what DOS can do. Even though these large programs are
  111.              written in C, they all bypass the C i/o functions. That does not
  112.              mean that they go down to the assembler level, however.
  113.  
  114.  
  115.              INTERRUPTS
  116.  
  117.              You have done a few interrupts. They call the standard DOS or
  118.              BIOS functions. Remember, they do this by going into low memory
  119.              (the first 1k of memory) and getting the address of the
  120.              subprogram that handles that particular interrupt. However, you
  121.              do not need to call these interrupts from the assembler level.
  122.              All modern compilers support interrupt calls in the language. If
  123.              yours doesn't, you need a more recent compiler. Before going on
  124.              with this chapter you need to read your compiler documentation
  125.              about interrupts. TURBO Pascal has INTR, QuickBASIC and QuickC
  126.              have INT86.  Read the documentation now. 
  127.  
  128.  
  129.  
  130.  
  131.  
  132.              The PC Assembler Tutor                                        270
  133.              ______________________
  134.  
  135.              Have you read it? No cheating is allowed, because you won't
  136.              understand the rest of this if you haven't read it. 
  137.  
  138.              Though technically C is a structure and Pascal is a record, they
  139.              are actually arrays where each array element has a specific name.
  140.              The interrupt routine reads all these values into the
  141.              corresponding register, calls the interrupt, then reads the
  142.              register values back into the array. Some languages have one
  143.              array for the input and another for the output. Int 21h is
  144.              special so QuickC has a special function called INTDOS. It is the
  145.              same as using Int 21h.
  146.  
  147.              The order of registers in the array is arbitrary and language
  148.              dependent. For TurboPascal it is (AX, BX, CX, DX, BP, SI, DI, DS,
  149.              ES, FLAGS). You enter values in the registers specified by the
  150.              interrupt, and then call the interrupt. The routine does the
  151.              rest. 
  152.  
  153.                  INTR ( int_no: byte, var the_regs: Registers)
  154.  
  155.              This will push the interrupt number, then the array address. On
  156.              entry to the interrupt call and after initializing BP, we will
  157.              have:
  158.                       int_no              bp + 6
  159.                       array_address       bp + 4
  160.                       old IP              bp + 2
  161.                 bp -> old BP              bp
  162.  
  163.              What follows is not the exact code, but is similar to what the
  164.              Pascal routine does:
  165.  
  166.                  ; - - - - - - - - - - 
  167.                  intr proc near
  168.  
  169.                       push bp
  170.                       mov  bp, sp
  171.                       push ax   ; save all registers except SP, DS, SS, CS
  172.                       push bx
  173.                       push cx
  174.                       push dx
  175.                       push si
  176.                       push di
  177.                       push bp   ; this is OUR bp
  178.                       push es
  179.  
  180.                       ; insert the interrupt number in the interrupt
  181.                       mov  al, [bp+6]  ; AL now contains the interrupt number
  182.                       lea  si, interrupt_spot  ; where the interrupt is
  183.                       mov  cs:[si+1], al       ; insert it in the interrupt
  184.  
  185.                       ; change all the registers
  186.                       mov  si, [bp+4]     ; array address is DS:SI
  187.                       mov  ax, [si]
  188.                       mov  bx, [si+2]
  189.                       mov  cx, [si+4]
  190.                       mov  dx, [si+6]
  191.                       mov  bp, [si+8]
  192.  
  193.  
  194.  
  195.  
  196.              Chapter 25 - What Does It All Mean?                           271
  197.              ___________________________________
  198.  
  199.                       mov  di, [si+12]
  200.                       mov  es, [si+16]
  201.  
  202.                       ; special manipulation for DS and SI
  203.                       push ds             ; save ds
  204.                       push si             ; save si
  205.                       push ax             ; temp save of ax from array
  206.                       mov  ax, [si+14]         ; ds from array to ax
  207.                       mov  si, [si+10]         ; si from array to si
  208.                       mov  ds, ax              ; now move ax to ds
  209.                       pop  ax                  ; restore ax
  210.  
  211.                       ; call the interrupt
  212.              interupt_spot:
  213.                       int  0         ; dummy number for the interrupt
  214.  
  215.                       ; special needs for SI and DS
  216.                       ; our SI and DS are at the top of the stack
  217.                       ; save values of flags, si and ds from interrupt
  218.                       pushf          ; value from interrupt
  219.                       push si        ; value from interrupt
  220.                       push ds        ; value from interrupt
  221.                       add  sp, 6     ; get to our si and ds
  222.                       pop  si        ; our si
  223.                       pop  ds        ; our ds
  224.                       sub  sp, 10    ; sp is where it was a moment ago.
  225.                       mov  [si], ax  ; DS:SI points to array
  226.                       mov  [si+2], bx
  227.                       mov  [si+4], cx
  228.                       mov  [si+6], dx
  229.                       mov  [si+8], bp
  230.                       mov  [si+12], di
  231.                       mov  [si+16], es
  232.                       pop  [si+14]   ; ds from the interrupt
  233.                       pop  [si+10]   ; si from the interrupt
  234.                       pop  [si+18]   ; flags from the interrupt
  235.                       add  sp, 4     ; skip our DS and SI (already in regs)
  236.  
  237.                       pop  es
  238.                       pop  bp        ; this is OUR bp
  239.                       pop  di
  240.                       pop  si
  241.                       pop  dx
  242.                       pop  cx
  243.                       pop  bx
  244.                       pop  ax
  245.  
  246.                       mov  sp, bp
  247.                       pop  bp
  248.  
  249.                       ret (4)        ; clear arguments off the stack
  250.                       ; - - - - - - - - - - - - - - - - - - - - 
  251.  
  252.              This should test your insight into using code. DS and SI are
  253.              needed for moving data, so we use some kludges to get it to work.
  254.  
  255.              There are two things here that you shouldn't normally do. First,
  256.  
  257.  
  258.  
  259.  
  260.              The PC Assembler Tutor                                        272
  261.              ______________________
  262.  
  263.              we are inserting the interrupt number directly in the machine
  264.              code. Secondly, we are playing around with the value of SP. These
  265.              are rare exceptions and shouldn't occur in your own code unless
  266.              absolutely necessary. 
  267.  
  268.              The first thing you are going to say is, "Gosh, that's a lot of
  269.              code for one interrupt." True, especially when the interrupt is
  270.              interrupt 12h. Here's int 12h inside of our template file:
  271.  
  272.                  ; - - - - - START CODE HERE
  273.                       int  12h       ; machine memory  (return in ax)
  274.                       call print unsigned
  275.                  ; - - - - - END CODE HERE
  276.  
  277.              It finds out how much memory your computer has and returns the
  278.              number of kbytes in AX. But how much extra time does using this
  279.              Pascal interrupt routine take? About 700 clocks or about .0002
  280.              seconds (that's right, 2 ten thousandths) on the slowest machine.
  281.              How many times will you call it during a program? Only one time.
  282.              There is no point in going down to the assembler level to write a
  283.              program that saves you .0002 seconds. In Pascal, you would write:
  284.  
  285.                  INTR ( $12 , the_regs) ;
  286.  
  287.              and be done with it. No big loss of time and no trouble at all. 
  288.  
  289.  
  290.              In fact, as far as I can see, there is no reason for doing any
  291.              interrupts from the assembler level. You may want to do a whole
  292.              subprogram that contains interrupts, but if you just need one or
  293.              two interrupts, it is easier to work from inside the high-level
  294.              language. 
  295.  
  296.              This includes the i/o we were talking about a minute ago. Yoy can
  297.              write a screen program inside a high level language using arrays.
  298.              Just think of a screen as a 80X25 array. If a two dimensional
  299.              array is too slow you need to go to a one dimensional array. All
  300.              interrupts that tell what kind of video card is in the computer,
  301.              what mode the screen is in, etc. can be done from the high-level
  302.              language. The most you need assembler for (depending on the
  303.              language) is moving the text array into video memory. You want a
  304.              bunch of help screens? Put all the help screens in a single file
  305.              and use the interrupt for random access file read to read a
  306.              screen when you need it.{2}
  307.  
  308.              Anything else?  Yes, we still have the need for speed. There are
  309.              certain types of operations like block moves of data, word
  310.              searches and sorting of arrays that are characterized by large
  311.              amounts of data and/or large amounts of computation. If you think
  312.              you see a way to use registers effectively for one of these
  313.              ____________________
  314.  
  315.                 2. What you actually want to do is have the first block of
  316.              data in the file tell you where each screen is and how long its
  317.              data is. Then the first 2 bytes or words of the screen data
  318.              should say the dimensions of the screen data ( 12 X 25, 17 X 3,
  319.              etc.). This will allow you to store and use screens of any size.
  320.  
  321.  
  322.  
  323.  
  324.              Chapter 25 - What Does It All Mean?                           273
  325.              ___________________________________
  326.  
  327.              things, you probably can beat a compiled version of the
  328.              subprogram. Then the only question is whether or not it is worth
  329.              the trouble.
  330.  
  331.              We have used the words "fast" and "slow" ambiguously so far, but
  332.              now it is time to quantify them. Before you get the numbers, you
  333.              need to know one thing about memory. People always talk about the
  334.              "data bus". What is it? It is a group of wires connecting the
  335.              80x86 chip to memory. The 8088 has 8 wires, the 8086, 80286 and
  336.              80386/SX have 16 wires, and the 80386 has 32 wires. That means
  337.              that the 8088 can transfer 8 bits of information at one time, the
  338.              8086 et. al. can transfer 16 bits at a time and the 80386 can
  339.              transfer 32 bits at a time. This means one byte, two byte and
  340.              four byte transfers respectively. This also means that the memory
  341.              bytes are ordered a little differently. You will never notice it
  342.              externally, but here is the different internal ordering.
  343.  
  344.              The 8088 has all bytes one after the other. All memory
  345.              read/writes are done with the same 8 wires:
  346.  
  347.                            8088
  348.                      MEMORY ADDRESSES
  349.  
  350.                            00005
  351.                            00004
  352.                            00003
  353.                            00002
  354.                            00001
  355.                            00000
  356.              data lines   ||||||||   (8 bits)
  357.  
  358.              (All our examples will use absolute memory locations starting at
  359.              00000). The chips with a 16 bit data bus have all the even
  360.              locations on the first 8 wires and the odd locations on the other
  361.              8 wires. They come in pairs - first even then odd:
  362.  
  363.                                 8086
  364.                           MEMORY ADDRESSES
  365.  
  366.                            00006     00007
  367.                            00004     00005
  368.                            00002     00003
  369.                            00000     00001
  370.              data lines   ||||||||  ||||||||   (16 bits)
  371.  
  372.              When one of these chips reads or writes, it can read/write either
  373.              the left or the right byte or the whole word. What it cannot do
  374.              is read the right byte from one pair along with the left byte
  375.              from another pair. If you want to read the word at 00005:00006,
  376.              the 8086 must:
  377.  
  378.                  1) read the 00005 byte.
  379.                  2) read the 00006 byte.
  380.                  3) join them together.
  381.  
  382.              This takes longer than just a single word read.
  383.  
  384.  
  385.  
  386.  
  387.  
  388.              The PC Assembler Tutor                                        274
  389.              ______________________
  390.  
  391.              The true 80386 has a 32 bit data bus. This allows it to read 4
  392.              bytes at a time, and its physical memory structure looks like
  393.              this:
  394.  
  395.                                           80386
  396.                                      MEMORY ADDRESSES
  397.  
  398.                            00010     00011     00012     00013
  399.                            0000C     0000D     0000E     0000F
  400.                            00008     00009     0000A     0000B
  401.                            00004     00005     00006     00007
  402.                            00000     00001     00002     00003
  403.              data lines   ||||||||  ||||||||  ||||||||  ||||||||  (32 bits)
  404.  
  405.              Instead of memory pairs, we now have memory quadruplets. As long
  406.              as a word is totally inside of one quadruplet, the read/write
  407.              time will be unaffected. If the read/write crosses the boundary
  408.              (as we did above), the read/write time will be affected in the
  409.              same way. The 80386 can also read 4 byte data quickly as long as
  410.              the total data is inside of one memory quadruplet. 
  411.  
  412.              In the 8086 family, data can always be read across these
  413.              boundaries but it takes more time. (On the IBM 370, on the other
  414.              hand, there are instructions that REQUIRE that data be aligned
  415.              along 32 bit boundaries).
  416.  
  417.              This means you should order your data in the following way in the
  418.              data segment:
  419.  
  420.                  QWORD DATA
  421.                  DWORD DATA
  422.                  TBYTE DATA     ; this is for the 8087
  423.                  WORD DATA
  424.                  BYTE DATA      ; all strings, etc.
  425.  
  426.              This insures that any read/write for that type of data will
  427.              always be as fast as possible. If the segment  definition has no
  428.              alignment type, it will start on a paragraph boundary - i.e.
  429.              every 16 bytes, and will work with anything. {3}
  430.  
  431.              In addition, if you ever subtract a number from SP to provide for
  432.              a temporary data area, it should always be an even number. If SP
  433.              is at an odd address instead of an even address, it takes longer
  434.              for PUSHes and POPs. Also, when you define the size of the stack
  435.              segment, it should be an even number of bytes.
  436.  
  437.              Having said that, it is now time for you to see the speeds of
  438.              ____________________
  439.  
  440.                 3. The alignment type is a word after the word SEGMENT which
  441.              says how the segment should be aligned. The following:
  442.  
  443.                       DATASTUFF SEGMENT BYTE PUBLIC 'DATA'
  444.  
  445.              says the segment can be aligned at any byte. The allowable forms
  446.              are BYTE, WORD, DWORD, PARA, PAGE (256 bytes). If there is no
  447.              explicit type, the default is PARA.
  448.  
  449.  
  450.  
  451.  
  452.              Chapter 25 - What Does It All Mean?                           275
  453.              ___________________________________
  454.  
  455.              instructions. Read the introduction to APPENDIX III, then glance
  456.              at the times to get the general idea of how fast times are. Come
  457.              back to this chapter when you are comfortable with what the times
  458.              look like. 
  459.  
  460.              Have you read APPENDIX III? If not, do it before going on.
  461.  
  462.              The compiled languages all have one thing in common. They tell
  463.              you that if you are writing a subroutine, you need to return from
  464.              the subroutine with DS, BP, SS, and SP unchanged. They don't say
  465.              a thing about any of the other registers. One thing this tells us
  466.              is that they are doing everything from memory locations, not
  467.              register locations. If you have taken a good look at the
  468.              execution times, you will have noticed the phenomenal difference
  469.              in time between a "memory, register" addition and a "register,
  470.              register" addition. 
  471.  
  472.              Now, if all you are going to do is move a number to a register,
  473.              add it, and move it out again, a compiler can do it as fast as
  474.              you can. But, if you run into a situation where you can use three
  475.              or four registers at the same time, you can cut the execution
  476.              time drastically. Compilers really can't use registers as
  477.              efficiently as we can (yet). This is an ideal spot for using
  478.              assembly language.
  479.  
  480.              The old adage that 10% of the code uses 90% of the computer time
  481.              is appropriate here. You now know about assembler language, and
  482.              you know what you want to do with it, so go out and enjoy. But
  483.              before you do, try to slog your way through the next chapter on
  484.              "simplified" segment definitions and linking to high level
  485.              languages. 
  486.  
  487.